import { wxToast } from '@/utils/wxUtil'
import { getCurrentPageUrlWithArgs } from '@/utils/util'
import { host } from '@/utils/config'
import { getToken, removeToken, removeHardID, getHardID } from '@/utils/storage'

/**
 * 判断请求状态是否成功
 * @param {http状态码} state
 * @return Boolen 
 */
const isHttpSuccess = (state) => {
  return state >= 200 && state < 300 || state === 304;
}

/**
 * 未登录情况操作
 * @param {String} url 当前页面链接
 * @param {String} loginUrl 登录页面链接
 */
const noLogin = (url = getCurrentPageUrlWithArgs(), loginUrl = '/pages/auth/index') => {
  if (getApp().globalData.isSinglePage) {
    wxToast({ title: '请前往小程序体验更多功能' })

    return
  }

  removeToken()
  removeHardID()

  wxToast({
    title: '未登录，正在跳转登录',
    duration: 2000,
    onClose: () => {
      getApp().globalData.loginAfterUrl = url
      wx.redirectTo({
        url: loginUrl
      })
    }
  })
}

const request = function (params = {}, url = '') {
  const isLoadHint = params.hasOwnProperty('load') && typeof (params.load) === 'string'
  isLoadHint && wx.showLoading({ title: params.load, mask: true })

  return new Promise((resolve, reject) => {
    wx.request({
      url: host + url,
      method: params.method || 'GET',
      data: params.data || {},
      header: {
        'content-type': 'application/json',
        'X-Token': getToken()
      },
      success: (res) => {
        isLoadHint && wx.hideLoading()
        if (isHttpSuccess(res.statusCode)) {
          if ([0].includes(res.data.Status.StatusCode)) {
            // 数据返回成功
            resolve(res.data)
          } else if (res.data.Status.StatusCode === 2000) {
            // 未登录
            noLogin(params.goUrl)
            reject({ data: res, type: 'LOGIN' })
          } else {
            !params.noToast && wxToast({
              title: typeof (res.data.Status.Msg2Client) === 'string' ? res.data.Status.Msg2Client : '未知错误',
              duration: 2000,
              onClose: () => {
                params.back && wx.navigateBack()
              }
            })
            reject({ data: res, type: 'API' })
          }
        } else {
          !params.noToast && wxToast({
            title: `请求失败${res.statusCode}`,
            duration: 2000
          })
          reject({ data: res, type: 'HTTP' })
        }
      },
      fail: (err) => {
        isLoadHint && wx.hideLoading()
        reject({ data: err, type: 'FAIL' })
      }
    })
  })
}

const get = (params, url) => request(Object.assign({ method: 'GET' }, params), url)

const post = (params, url) => request(Object.assign({ method: 'POST' }, params), url)

/**
 * 特殊请求接口
 * @param {string} CmdCode 接口名称
 * @param {object} data 荷载
 * @param {object} config 请求配置
 */
const cfunc = (CmdCode, data = {}, config) => {
  const HardID = getHardID()
  const Token = getToken()

  return post(
    Object.assign({}, config, {
      data: {
        Header: {
          CmdCode,
          HardID,
        },
        Token,
        ...data
      }
    }),
    '/cfunc'
  )
}

// 请求共享，可用于
// 当一个页面同时渲染多个内部自获取数据的组件时；
// 在预加载数据的情况下，还未完成预加载就进入了预加载页面；
const pendingPromises = {}
const requestShare = function (params = {}, url = '') {
  const requestKey = JSON.stringify([params, url])
  if (pendingPromises[requestKey]) return pendingPromises[requestKey]
  
  const thePromise = request(params, url)
  .finally(() => {
    delete pendingPromises[requestKey]
  })

  return pendingPromises[requestKey] = thePromise
}

// 请求锁定，发送请求后锁定（后续点击会返回reject，会被catch捕获），等待请求结束后解除，防止重复操作
const pendingLock = {}
const requestLock = function (params = {}, url = '') {
  const requestKey = JSON.stringify([params, url])
  if (pendingLock[requestKey]) return Promise.reject({ type: 'LOCK' })

  pendingLock[requestKey] = true

  return request(params, url).finally(() => {
    delete pendingLock[requestKey]
  })
}

const getLock = (params, url) => requestLock(Object.assign({ method: 'GET' }, params), url)

const postLock = (params, url) => requestLock(Object.assign({ method: 'POST' }, params), url)

/**
 * 微信上传文件Promise化，参数请参考微信uploadFile接口
 */
function uploadFileAsync ({ url, filePath, name, header, formData, timeout, useHighPerformanceMode, enableProfile, enableHttp2, complete } = {}) {
  return new Promise((resolve, reject) => {
    wx.uploadFile({
      url,
      filePath,
      name,
      header: Object.assign({}, header, {
        'X-Token': getToken()
      }),
      formData,
      timeout,
      useHighPerformanceMode,
      enableProfile,
      enableHttp2,
      complete,
      success: res => {
        const data = JSON.parse(res.data)

        if (data.state === 1) {
          resolve(data.result)
        } else {
          res.data = data
          reject({ data: res, type: 'API' })
        }
      },
      fail: err => {
        reject({ data: err, type: 'FAIL' })
      }
    })
  })
}

export {
  request,
  get,
  post,
  cfunc,
  requestShare,
  requestLock,
  getLock,
  postLock,
  uploadFileAsync
}
